import Mota, { MotaInterface } from "@/mota";
import { GetImage, ImageName } from "@/mota/data/ImageLoader";
import { DrawSize, EventListType, GetAllMap, ImageState, MapFloor, MapInfo, ParsrFloorInfo, UnitInfo } from "@/mota/data/MapDataUtils";

export type MapInfoMap = Map<MapFloor, MapInfo>;

export default class MapUtils implements MotaInterface {

	public Mota: Mota;

	public mapData = GetAllMap();

	public floor: number = 1;

	public map: MapInfo = { floor: -9999, name: "error", unit: new Set };

	public timeMax: number = 20;

	public time: number = 0;

	public ZIndex: number = 2;

	constructor(mota: Mota) {
		this.Mota = mota;
		this.LoadMap(this.floor);
	}

	public GetMap(floor: MapFloor) {
		return this.mapData.get(floor);
	}

	public LoadMap(floor: number, order?: boolean): boolean {
		const { time, timeMax, Mota: { hero, tip } } = this;
		if (time > 0) {
			return false;
		}
		this.time = timeMax;
		const map = this.GetMap(floor);
		if (map === undefined) {
			tip.AddMessage(`目的地图为未知区域`);
			return false;
		} else {
			const orderType = (order !== undefined ? order : this.floor > floor) ? "楼梯上" : "楼梯下";
			const targetUnit = Array.from(map.unit).find(info => info[0] === orderType);
			this.floor = floor;
			this.map = map;
			if (targetUnit === undefined) {
				tip.AddMessage(`目的地图无进出楼梯`);
				return false;
			} else {
				const [, , [x, y]] = targetUnit;
				hero.X.Value = x;
				hero.X.HasNext = false;
				hero.Y.Value = y;
				hero.Y.HasNext = false;
				tip.AddFloor(map.name);
			}
			return true;
		}
	}

	public ResetMapMap(info: MapInfo) {
		const parseInfo = ParsrFloorInfo(info);
		this.mapData.set(info.floor, parseInfo);
		if (info.floor === this.floor) {
			this.map = parseInfo;
		}
	}

	public EnterMap(floor: number, order?: boolean) {
		this.LoadMap(floor, order);
	}

	public EqualsSize(tx: number, ty: number, size: DrawSize): boolean {
		if (size.length === 2) {
			const [x1, y1] = size;
			if (tx === x1 && ty === y1) {
				return true;
			}
		} else {
			const [x1, y1, x2, y2] = size;
			if (tx >= x1 && ty >= y1 && tx <= x2 && ty <= y2) {
				return true;
			}
		}
		return false;
	}

	public GetShowUnit(tx: number, ty: number, floor?: number): UnitInfo | undefined {
		const list = this.GetUnit(tx, ty, floor);
		if (list === undefined) { return undefined; }

		for (const info of list) {
			if (info[1] === true) { return info; }
		}
		return undefined;
	}

	public GetUnitByName(tx: number, ty: number, image: ImageName, floor?: number): UnitInfo | undefined {
		const { Mota: { tip } } = this;
		const list = this.GetUnit(tx, ty, floor);
		if (list === undefined) {
			tip.AddMessage(`第${floor}层无${image}：[${tx},${ty}]`);
			return undefined;
		}
		for (const info of list) {
			if (info[0] === image) { return info; }
		}
		return undefined;
	}

	public GetUnit(tx: number, ty: number, floor?: MapFloor): UnitInfo[] | undefined {
		const map = floor === undefined || floor === this.floor ? this.map : this.GetMap(floor);
		if (map === undefined) { return undefined; }
		return Array.from(map.unit).reverse().filter(([, , size]) => this.EqualsSize(tx, ty, size));
	}

	public ChangeUnitShow(tx: number, ty: number, image: ImageName, show: boolean, floor?: number): void {
		const unit = this.GetUnitByName(tx, ty, image, floor);
		if (unit !== undefined) {
			unit[1] = show;
		}
	}

	public UnitShow(tx: number, ty: number, image: ImageName, floor?: number): Promise<void> {
		return new Promise((res) => {
			const { Mota } = this;
			const { speedUtils: { speed } } = Mota;
			const unit = this.GetUnitByName(tx, ty, image, floor);
			if (unit === undefined) { return; }

			const count = 3; // TODO: need meke new Speed
			let i = 0;
			const remove = Mota.AddTrigger(() => {
				i += 1 / count;
				if (i >= count) {
					unit[1] = true;
					remove();
					res();
				} else {
					unit[1] = i / count;
				}
			});
		});
	}

	public UnitHidden(tx: number, ty: number, image: ImageName, floor?: number): Promise<void> {
		return new Promise((res) => {
			const { Mota } = this;
			const { speedUtils: { speed } } = Mota;
			const unit = this.GetUnitByName(tx, ty, image, floor);
			if (unit === undefined) { return; }

			const count = 2;
			let i = count;
			const remove = Mota.AddTrigger(() => {
				i -= 1 / count;
				if (i < 0) {
					unit[3] = "first";
					remove();
					res();
				} else {
					unit[1] = true;
					unit[1] = i / count;
				}
			});
		});
	}

	public UnitLeave(tx: number, ty: number, image: ImageName, floor?: number): Promise<void> {
		return new Promise((res) => {
			const { Mota } = this;
			const { speedUtils: { speed2 } } = Mota;
			const unit = this.GetUnitByName(tx, ty, image, floor);
			if (unit === undefined) { return; }

			const speed = speed2.Value;
			const max = GetImage(image)[1].length;
			const count = max * speed;
			let i = 0;
			const remove = Mota.AddTrigger(() => {
				i += 1;
				if (i >= count) {
					unit[1] = false;
					remove();
					res();
				} else {
					unit[3] = Math.floor(i / speed);
				}
			});
		});
	}

	public UnitEnter(tx: number, ty: number, image: ImageName, floor?: number): Promise<void> {
		return new Promise((res) => {
			const { Mota } = this;
			const { speedUtils: { speed2 } } = Mota;
			const unit = this.GetUnitByName(tx, ty, image, floor);
			if (unit === undefined) { return; }

			const speed = speed2.Value;
			const max = GetImage(image)[1].length;
			let i = max * speed;
			const remove = Mota.AddTrigger(() => {
				i -= 1;
				if (i < 0) {
					unit[3] = "first";
					remove();
					res();
				} else {
					unit[1] = true;
					unit[3] = Math.floor(i / speed);
				}
			});
		});
	}

	public Draw: MotaInterface["Draw"] = (draw) => {
		const { Mota: { onBuild } } = this;
		const { _2d } = draw;
		if (onBuild) { return; }

		_2d.save();
		draw.translate(4, 0);

		this.map.unit.forEach((unit) => draw.DrawUnti(unit));

		_2d.restore();
	}

	public Trigger(): void {
		let { time, Mota: { speedUtils: { speed2: { Value: speed2 } } } } = this;
		if (time > 0) {
			time -= speed2;
			if (time < 0) {
				time = 0;
			}
			this.time = time;
		}
	}
}
